/**
 * @brief 有人套接字分发协议实现模块。
 * @version 1.0
 * @date 2018-2-28
 * @author 宋炜
 * @ref 4g_sdp.pdf 套接字分发协议说明书  V1.0
*/
#ifndef SDP_HPP
#define SDP_HPP
#include <string>

class sdp
{
public:
	enum type { SERIAL, NET, RESPOND };   // SERIAL , 串口数据包； NET 网络数据包； RESPOND , 模块响应数据包
	// 目标端口类型
	enum net { SOCK1 = 0x61, SOCK2, SOCK3, SOCK4, HTTP };
	// 响应码
	enum errCode { E_HEAD = 0X00, E_LENGTH, E_PARAM, E_LRC, E_SEND };

	struct stSDP {
		/**
		*/
		stSDP();
		// 打包操作
		stSDP(type t, const char* data, size_t len, net sock);
		~stSDP();
		/**
		* @brief 构造错误包
		*/
		stSDP& Error(errCode e);
		/**
		 * @brief 构造网络返回的数据包
		*/
		stSDP& Net(net sock, const char* data, size_t len);
		/**
		 * @brief 获取要通过网络发送数据
		 * @data, 要发送的数据
		 * @return 成功返回数据长度，否则返回数据0
		*/
		size_t Data(void** data);
		/**
		* @brief 解析数据包。
		* @param from , 要解析的数据包
		* @return
		*/
		stSDP& Data(const char* from, size_t sum_len);
		/**
		* @brief 获取数据包的平台结构数据
		*/
		size_t PackRaw(void** data);
		/**
		 * @brief 如果数据包解析正确返回true;
		*/
		operator bool() { return m_is_ok; }
		/**
		 * @brief 计算LRC。
		 * @param data ， 要计算的数据
		*/
		uint8_t makeLrc(stSDP& obj);

		union {
			int head : 24;            // 标记头
			uint8_t  alise_head[3];
		}__Head;

		uint16_t    len;
		uint8_t     param1;         // socket 标记0x61 SOCKA, 0X62 SOCKB;

		struct
		{
			int : 7;
			int     from : 1;         // 描述返回数据的来源，1模块返回；0网络返回
		} param2;

		union
		{
			void* data;       // 数据
			uint8_t     err;
		} __Data;

		uint8_t     lrc;            // 和校验
			// 如果数据是解析的数据，那么这个值代表解析是否正确。
			// 对于打包的数据处理，这个数值没有意义
		bool        m_is_ok;
	}__attribute__((packed));

private:
	stSDP    m_data;
public:
	sdp();
	virtual ~sdp();
	/**
	 * @brief 解析数据包。
	*/
	bool Unpack(const char* data, size_t len);
	/**
	 * @brief 获取原始的数据。不包含数据包头和其他结构数据.这个函数主要用于
	 *       获取串口数据包的解析操作后读取原始的数据向网络发送
	 * @param
	 * @return
	*/
	size_t GetData(void** data);
	/**
	 * @brief 获取数据包平坦结构数据。包含了所有的数据包结构，这个函数主要
	 *        用于向串口发送带有网络信息指示或者数据响应包
	 * @param Data ， 返回的数据指针
	 * @return 成功返回数据长度，否则返回 0
	*/
	size_t GetPackRaw(void** data);
	/**
	 * @brief 构造错误数据包
	*/
	void Error(errCode e);
	/**
	 * @brief net数据打包。
	 * @param sock , 数据包接口
	 * @param data, 要打包的数据
	 * @param len, 要打包的数据长度
	*/
	void Net(net sock, const void* data, size_t len);
	int socket()
	{
		return m_data.param1 - 0x60;
	}
private:
	/**
	 * @brief 打包。
	 * @param len
	 * @param raw,
	 * @param rlen
	 * @param result
	 */
	bool packet_serial(size_t len, const char* raw, size_t& rlen, char** result);
	bool packet_net(size_t len, const char* raw, size_t& rlen, char** result);
	/**
	 * @brief 解包。这个函数在DTU中无用。暂时留个定义
	 */
	bool unpacket(size_t len, const char* data);
	/**
	 * @brief 检查数据结构和LRC是否正确。如果正确则执行相关指令
	 */
	bool check();
};

#endif // SDP_HPP
